package com.bcinfo.iccard.busi;

import java.io.IOException;
import java.text.MessageFormat;
import java.util.Arrays;
import java.util.Properties;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.PropertiesFactoryBean;

import com.bcinfo.iccard.enums.Constants;
import com.bcinfo.iccard.exception.IccardDatabaseException;
import com.bcinfo.iccard.model.Merchant;
import com.bcinfo.iccard.model.Pos;
import com.bcinfo.iccard.model.RechargeableCard;
import com.bcinfo.iccard.service.ComboCardTradeService;
import com.bcinfo.iccard.service.MerchantService;
import com.bcinfo.iccard.service.PosService;
import com.bcinfo.iccard.service.RechargeCardTradeService;
import com.bcinfo.iccard.service.RechargeableCardService;
import com.bcinfo.iccard.util.AESCryptor;
import com.bcinfo.iccard.util.DataCryptor;
import com.bcinfo.wapportal.util.Encryption;
import com.solab.iso8583.IsoMessage;
import com.solab.iso8583.IsoType;
/**
 * 抽象业务处理类 <br/>
 * <p>Description:<br/>
 *    <p>TODO 详细描述
 *
 * <p>For Examples <br/>
 * <PRE>
 * </PRE>
 * <p>
 */
public abstract class AbstractBusinessHandler implements BusinessHandler {
	
	@Autowired
    private PropertiesFactoryBean propertiesFactoryBean;
	@Autowired
    protected MerchantService merchantService;
    @Autowired
    protected PosService posService;
    @Autowired
    protected RechargeableCardService rechargeableCardService;
    @Autowired
    protected RechargeCardTradeService rechargeCardTradeService;
    @Autowired
    protected ComboCardTradeService comboCardTradeService;
	/**
	 * 系统配置读取
	 *  <br/>
	 * <p>Description: TODO(详细描述) 
	 * <br/>
	 * <p>Author: wangguangchun<br/>
	 * <p>Date: 2013-12-9-上午10:05:43<br/>
	 * <p>
	 * @param configKey 配置KEY
	 * @return   
	 *
	 */
	protected String getConfig(String configKey) 
    {
        try
        {
            return ((Properties) (propertiesFactoryBean.getObject())).getProperty(configKey);
        }
        catch (IOException e)
        {
            _logger.error(ExceptionUtils.getStackTrace(e));
           return null;
        }
    }
    protected Logger _logger=Logger.getLogger(AbstractBusinessHandler.class);
	@Override
    public final void invokeHandleBusiness(IsoMessage req, IsoMessage resp) 
    {
	    try
        {
	      //检查POS机信息及商家信息是否合法
	        Pos pos = checkMerAndPos(req, resp);
	        //找到匹配的POS信息
	        if(pos!=null){
	            String macKey = pos.getMak();
	            // MAC校验
	            boolean authPass = authenticate(req, macKey);
	            if (authPass)
	            {
	                //若存在最后一笔正确交易，则进行纠正操作，将该商家pos机的从最后正确一笔到现在的所有非结算交易进行纠正操作(充值卡交易需返还金额，套餐卡直接修改领取状态)
	                if(req.hasField(62) &&null !=req.getField(62).getValue()){
	                    String lastTrade=(String)req.getField(62).getValue();
	                    if(lastTrade!=null && lastTrade.length()==13){
	                        if(_logger.isDebugEnabled()){
	                            _logger.debug(MessageFormat.format("got last serialNum[()] from request",lastTrade ));
	                        }
	                       // String tradeType=lastTrade.substring(0,1);
	                        String tradeNo=lastTrade.substring(1);
	                        //交易交替进行，会导致交易遗漏，所以统一生成版本号，统一进行拨正操作
	                       // if("0".equals(tradeType)){
	                            //充值卡纠正
	                            rechargeCardTradeService.feedBackFailedTradeByMerPos(pos.getMerchantCode(), pos.getPosCode(), tradeNo);
	                        //}else if("1".equals(tradeType)){
	                            //套餐卡纠正
	                            comboCardTradeService.feedBackFailedTradeByMer(pos.getMerchantCode(),pos.getPosCode(),tradeNo);
	                        //}
	                    } 
	                }
	                // 通过校验，则进行业务处理，由业务子类实现
	                handleBusiness(req, resp);
	            }
	            else
	            {
	                // 否则返回MAC校验失败
	                resp.setValue(39, Constants.ResponeCode.macKeyErr.value, IsoType.ALPHA, 2);
	            }
	        }
        }
        catch (Exception e)
        {
            _logger.error(ExceptionUtils.getStackTrace(e));
            resp.setValue(39, Constants.ResponeCode.timeOut.value, IsoType.ALPHA, 2);
        }
	    
    }
	/**
	 * 抽象业务处理方法
	 *  <br/>
	 * <p>Description: TODO(具体处理子类进行实现) 
	 * <br/>
	 * <p>Author: wangguangchun<br/>
	 * <p>Date: 2013-12-9-上午10:05:36<br/>
	 * <p>
	 * @param req 业务请求报文
	 * @param resp 业务响应报文  
	 * @throws Exception 
	 *
	 */
	public  abstract void handleBusiness(IsoMessage req,IsoMessage resp) throws Exception;
	/**
	 * MAC校验
	 *  <br/>
	 * <p>Description: MAC校验的默认实现，若具体业务类的校验规则不同，则由具体业务类重写本方法。
	 * <p>根据百富定义的MAC计算算法生成校验码，和报文中的校验码比较,若相等，则校验成功，否则失败.
	 * <br/>百富文档上要求请求报文除去 长度\tpdu\version\64域校验码外剩余的所有数据均参与计算
	 * <p>Author: wangguangchun<br/>
	 * <p>Date: 2013-12-9-上午10:05:32<br/>
	 * <p>
	 * @param req 业务请求报文
	 * @return  是否通过MAC校验 
	 */
    protected boolean authenticate(IsoMessage req, String macKey)
    {
        byte[] data = req.writeData();
        System.out.println("whole data:");
        DataCryptor.printByteArray(data);
        //j8583包处理binary的域使用了16字节存放8字节的内容，这里需要后期解决修改
        byte[] toMacBuf = Arrays.copyOfRange(data, 4, data.length -8);
        System.out.println("toMacBuf data:");
        DataCryptor.printByteArray(toMacBuf);
        byte[] mac = DataCryptor.XOR_DES_MAC_BINARY(macKey, DataCryptor.getHexStr(toMacBuf), 0);
        byte[] macBuf = (byte[]) req.getField(64).getValue();
        System.out.println("macBuf:");
        DataCryptor.printByteArray(macBuf);
        String macFromClient = DataCryptor.getHexStr(macBuf);
        if (mac != null && macFromClient != null && DataCryptor.getHexStr(mac).equals(macFromClient))
        {
            if(_logger.isDebugEnabled()){
                _logger.debug("authenticate passed for req "+req.toString());
            }
            return true;
        }
        else
        {
            _logger.error("authenticate failed! caculated mac is ["+DataCryptor.getHexStr(mac)+"],but the mac from client is ["+DataCryptor.getHexStr(macBuf)+"]");
            return false;
        }
    }
    /**
     * 
     * 检查商家信息和pos信息是否有效 <br/>
     * <p>Description: 从报文中获取merchantcode及poscode，在数据库中查询，若存在正确数据则继续，否则直接失败
     * <br/>该方法不可重写
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-9-上午11:16:41<br/>
     * <p>   
     *
     */
    protected final Pos checkMerAndPos(IsoMessage req,IsoMessage resp) throws Exception{
        String posCode=(String)req.getField(41).getValue();
        String mcCode=(String)req.getField(42).getValue();
        Merchant mers=posService.loadByMerchantEncode(mcCode);
        if(mers==null){
            _logger.error("merchant with mcCode["+mcCode+"] doesn`t exists or freezed.");
            resp.setValue(39,Constants.ResponeCode.cardAcceptorErr.value , IsoType.ALPHA, 2);
        }else{
            Pos pos=posService.loadByCode(posCode,mcCode);
            if(pos==null){
                _logger.error("pos with posCode["+posCode+"] doesn`t exists or freezed.");
                resp.setValue(39,Constants.ResponeCode.badTerminalId.value , IsoType.ALPHA, 2);
            }else{
                pos.setMerchantCode(mcCode);
                if(_logger.isDebugEnabled()){
                    _logger.debug("checkMerAndPos ok,pos="+pos.toString());
                }
                return pos;
            }
        }
        return null;
    }
    /**
     * 
     * 获取POS信息 <br/>
     * <p>Description: TODO(详细描述) 
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-9-上午11:39:21<br/>
     * <p>
     * @param req 请求报文
     * @return pos信息
     * @throws Exception   
     *
     */
    protected Pos getPos(IsoMessage req) throws Exception{
        String posCode=(String)req.getField(41).getValue();
        String mcCode=(String)req.getField(42).getValue();
        if(posCode==null || mcCode ==null){
            return null;
        }else{
            Pos pos=posService.loadByCode(posCode,mcCode);
            if(pos!=null){
                pos.setMerchantCode(mcCode);
            }
            return pos; 
        }
    }
    /**
     * 
     * 校验pin <br/>
     * <p>Description:  
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-9-上午11:53:59<br/>
     * <p>
     * @param pinBlockFromClient 请求报文中的pinBlock
     * @param pinKey 当前POS机pinkey
     * @param cardInfo 充值卡信息
     * @return 校验结果
     * @throws Exception   
     *
     */
    protected final boolean checkPin(byte[] pinBlockFromClient,String pinKey,RechargeableCard cardInfo)throws Exception{
        try
        {
            if(cardInfo.getRechargeableCardPwd()!=null){
                String mingPwd=AESCryptor.decrypt(cardInfo.getRechargeableCardPwd());
                byte[] pinBlock=DataCryptor.encryption(DataCryptor.getPinBlock(mingPwd, cardInfo.getRechargeableCardNum()) ,pinKey);
                if(Base64.encodeBase64String(pinBlock).equals(Base64.encodeBase64String(pinBlockFromClient))){
                    if(_logger.isDebugEnabled()){
                        _logger.debug("check pin succeed!");
                    }
                    return true;
                }else{
                    _logger.error("check pin failed ,pinBlock is :"+DataCryptor.getHexStr(pinBlock)+",but pinBlockFromClient is :"+DataCryptor.getHexStr(pinBlockFromClient));
                }
            }else{
                //无密码
                return false;
            }
            return false; 
        }
        catch (Exception e)
        {
            _logger.error(ExceptionUtils.getStackTrace(e));
            return false;
        }
        
    }
    /**
     * 检查卡信息
     *  <br/>
     * <p>Description: 根据卡号检查卡是否正常 
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-9-下午02:39:28<br/>
     * <p>
     * @throws Exception   
     *
     */
    protected RechargeableCard checkCardNo(IsoMessage req) {
        String cardNo=(String)req.getField(2).getValue();
        //根据卡号从数据库读取
        RechargeableCard card=null;
        try
        {
            card=rechargeableCardService.load(cardNo);
        }
        catch (IccardDatabaseException e)
        {
            _logger.error(ExceptionUtils.getStackTrace(e));
        }
        if(card!=null && card.getRechargeableCardStatus()==Constants.CardStatus.actived.value){
            return card;
        }else{
            return null;
        }
    }
    /**
     * 余额字段组装
     *  <br/>
     * <p>Description: 
     * <br/>账户类型(00-缺省)+余额类型(02-可用余额)+货币代码(156-人民币)+贷/借(C/D)+余额(分)
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-9-下午04:14:06<br/>
     * <p>
     * @param balance 余额
     * @return   
     *
     */
    protected String wrapBalance(long balance){
        
        return "0002156C"+balance;
    }
    /**
     * 解析余额字段
     *  <br/>
     * <p>Description:去掉前缀，只取数值部分
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-12-下午04:12:16<br/>
     * <p>
     * @param amount
     * @return   
     *
     */
    protected Long parseBalance(String amount){
        
        return NumberUtils.toLong(amount.replaceAll("0002156D", ""),0);
    }
    /**
     * 二维码内容解密
     * <p>Description: TODO(详细描述) 
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-12-下午04:12:11<br/>
     * <p>
     * @param cipherText 加密后的QRCODE内容
     * @return 明文二维码内容
     *
     */
    protected String parseQrCode(String cipherText){
        if(cipherText==null ||"".equals(cipherText)){
            return null;
        }else{
            try
            {
                return Encryption.decrypt(new StringBuffer(cipherText)).toString(); 
            }
            catch (Exception e)
            {
                return null;
            }
            
        }
    }
    /**
     * 生成流水号
     *  <br/>
     * <p>Description: TODO(详细描述) 
     * <br/>
     * <p>Author: wangguangchun<br/>
     * <p>Date: 2013-12-17-下午04:42:02<br/>
     * <p>
     * @return   
     *
     */
    protected String generateSerialNum() {
        try
        {
            return  posService.generateSerialNum();
        }
        catch (Exception e)
        {
            return null;
        }
       
    }
    
}
